﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using StackExchange.Redis;

namespace xBei.Redis.Extension {
    partial class RedisClient {
        #region 哈希表
        /// <summary>
        /// Hash表的字段（field）数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long HashLength(string key) => HashLengthAsync(key).Result;
        /// <summary>
        /// Hash表的字段（field）数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> HashLengthAsync(string key)
            => await (await GetDatabaseAsync()).HashLengthAsync(GetKey(key));
        /// <summary>
        /// Hash表的字段（field）时候存在
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public bool HashExists(string key, string field) => HashExistsAsync(key, field).Result;
        /// <summary>
        /// Hash表的字段（field）时候存在
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public async Task<bool> HashExistsAsync(string key, string field)
            => await (await GetDatabaseAsync()).HashExistsAsync(GetKey(key), field);
        /// <summary>
        /// 删除哈希表 key 中的一个域（field）
        /// 单纯执行，不返回
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public RedisClient HashDelete(string key, string field) => HashDeleteAsync(key, field).Result;
        /// <summary>
        /// 删除哈希表 key 中的一个域（field）
        /// 单纯执行，不返回
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public async Task<RedisClient> HashDeleteAsync(string key, string field) {
            await (await GetDatabaseAsync()).HashDeleteAsync(GetKey(key), field, CommandFlags.FireAndForget);
            return this;
        }
        /// <summary>
        /// 设置Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="seconds">超时时间（单位秒）0-表示永久存在</param>
        /// <returns></returns>
        public RedisClient HashSet(string key, string field, RedisValue value, int seconds = 0) => HashSetAsync(key, field, value, seconds).Result;
        /// <summary>
        /// 设置Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="seconds">超时时间（单位秒）0-表示永久存在</param>
        /// <returns></returns>
        public async Task<RedisClient> HashSetAsync(string key, string field, RedisValue value, int seconds = 0) {
            await (await GetDatabaseAsync()).HashSetAsync(GetKey(key), field, value, flags: CommandFlags.FireAndForget);
            if (seconds > 0) {
                await ExpireAsync(key, seconds);
            }
            return this;
        }
        /// <summary>
        /// 设置Hash表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public RedisClient HashSet(string key, IDictionary<string, RedisValue> data, int seconds = 0)
            => HashSetAsync(key, data, seconds).Result;
        /// <summary>
        /// 设置Hash表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public async Task<RedisClient> HashSetAsync(string key, IDictionary<string, RedisValue> data, int seconds = 0) {
            var cacheKey = GetKey(key);
            foreach (var item in data) {
                await (await GetDatabaseAsync()).HashSetAsync(cacheKey, item.Key, item.Value, flags: CommandFlags.FireAndForget);
            }
            if (seconds > 0) {
                await ExpireAsync(key, seconds);
            }
            return this;
        }
        /// <summary>
        /// 设置Hash表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public RedisClient HashSet<T>(string key, IDictionary<string, T> data, int seconds = 0) where T : class, new() 
            => HashSetAsync<T>(key, data, seconds).Result;

        /// <summary>
        /// 设置Hash表
        /// </summary>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public async Task<RedisClient> HashSetAsync<T>(string key, IDictionary<string, T> data, int seconds = 0) where T : class, new() {
            var cacheKey = GetKey(key);
            foreach (var item in data) {
                await (await GetDatabaseAsync()).HashSetAsync(cacheKey, item.Key, item.Value.Serialize(), flags: CommandFlags.FireAndForget);
            }
            if (seconds > 0) {
                await ExpireAsync(key, seconds);
            }
            return this;
        }
        /// <summary>
        /// 设置Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public RedisClient HashSet(string key, string field, DateTime value, int seconds = 0)
            => HashSetAsync(key, field, value.Ticks, seconds).Result;
        /// <summary>
        /// 设置Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public async Task<RedisClient> HashSetAsync(string key, string field, DateTime value, int seconds = 0)
            => await HashSetAsync(key, field, value.Ticks, seconds);
        /// <summary>
        /// 设置Hash表（key）字段（field）的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public RedisClient HashSet<T>(string key, string field, T value, int seconds = 0) where T : class, new()
            => HashSetAsync<T>(key, field, value, seconds).Result;
        /// <summary>
        /// 设置Hash表（key）字段（field）的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="seconds"></param>
        /// <returns></returns>
        public async Task<RedisClient> HashSetAsync<T>(string key, string field, T value, int seconds = 0) where T : class, new() {
            await (await GetDatabaseAsync()).HashSetAsync(GetKey(key), field, value.Serialize(), flags: CommandFlags.FireAndForget);
            if (seconds > 0) {
                await ExpireAsync(key, seconds);
            }
            return this;
        }
        /// <summary>
        /// 读取Hash表中所有数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public IDictionary<string, T?> HashGetAll<T>(string key) where T : class
            => HashGetAllAsync<T>(key).Result;
        /// <summary>
        /// 读取Hash表中所有数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<IDictionary<string, T?>> HashGetAllAsync<T>(string key) where T : class {
            var list = await (await GetDatabaseAsync())
                                .HashGetAllAsync(GetKey(key));
            return list.Where(ii => ii.Value.HasValue)
                       .ToDictionary(ii => ii.Name.ToString(), ii => ii.Value.ToString().Deserialize<T>());
        }
        /// <summary>
        /// 读取Hash表中所有数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IDictionary<string, RedisValue> HashGetAll(string key)
            => HashGetAllAsync(key).Result;
        /// <summary>
        /// 读取Hash表中所有数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<IDictionary<string, RedisValue>> HashGetAllAsync(string key) {
            return (await (await GetDatabaseAsync())
                .HashGetAllAsync(GetKey(key)))
                .Where(ii => ii.Value.HasValue)
                .ToDictionary(ii => ii.Name.ToString(), ii => ii.Value);
        }
        /// <summary>
        /// 读取Hash表中Field的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public T? HashGet<T>(string key, string field, T? df = default) where T : class, new()
            => HashGetAsync<T>(key, field, df).Result;
        /// <summary>
        /// 读取Hash表中Field的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public async Task<T?> HashGetAsync<T>(string key, string field, T? df = default) where T : class, new() {
            if (await HashExistsAsync(key, field))
                return (await (await GetDatabaseAsync()).HashGetAsync(GetKey(key), field)).ToString().Deserialize<T>();
            return df;
        }
        /// <summary>
        /// 读取Hash表中Field的数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public RedisValue HashGet(string key, string field, RedisValue df = default) => HashGetAsync(key, field, df).Result;
        /// <summary>
        /// 读取Hash表中Field的数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public async Task<RedisValue> HashGetAsync(string key, string field, RedisValue df = default) {
            if (await HashExistsAsync(key, field))
                return await (await GetDatabaseAsync()).HashGetAsync(GetKey(key), field);
            return df;
        }

        /// <summary>
        /// 读取Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="dv"></param>
        /// <returns></returns>
        public DateTime HashGet(string key, string field, DateTime dv)
            => HashGetAsync(key, field, dv).Result;
        /// <summary>
        /// 读取Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="dv"></param>
        /// <returns></returns>
        public async Task<DateTime> HashGetAsync(string key, string field, DateTime dv) {
            var r = await (await GetDatabaseAsync()).HashGetAsync(GetKey(key), field);
            if (r == RedisValue.Null) {
                return dv;
            }
            return new DateTime((long)r);
        }
        /// <summary>
        /// 读取Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Dictionary<string, RedisValue> HashGetObj(string key)
            => HashGetObjAsync(key).Result;
        /// <summary>
        /// 读取Hash表（key）字段（field）的值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Dictionary<string, RedisValue>> HashGetObjAsync(string key) {
            var db = await GetDatabaseAsync();
            var redisKey = GetKey(key);
            var he = await db.HashGetAllAsync(redisKey);
            var dic = new Dictionary<string, RedisValue>();
            foreach (var item in he) {
                var hashKey = (string?)item.Name;
                if (string.IsNullOrWhiteSpace(hashKey)) { continue; }
                dic.Add(hashKey, item.Value);
            }
            return dic;
        }
        /// <summary>
        /// 试试Hash字段，把找到的字段及其值返回
        /// </summary>
        /// <param name="key"></param>
        /// <param name="pattern"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageOffset"></param>
        /// <returns></returns>
        public HashScanResult<RedisValue> HashScan(string key, string pattern, int pageSize = 10, int pageOffset = 0)
            => HashScanAsync(key, pattern, pageSize, pageOffset).Result;
        /// <summary>
        /// 试试Hash字段，把找到的字段及其值返回
        /// </summary>
        /// <param name="key"></param>
        /// <param name="pattern"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageOffset"></param>
        /// <returns></returns>
        public async Task<HashScanResult<RedisValue>> HashScanAsync(string key, string pattern, int pageSize = 10, int pageOffset = 0) {
            var db = await GetDatabaseAsync();
            var query = db.HashScanAsync(GetKey(key), pattern, pageSize, pageOffset);

            var dic = new Dictionary<string, RedisValue>();
            await foreach (var item in query) {
                var hashKey = (string?)item.Name;
                if (string.IsNullOrWhiteSpace(hashKey)) { continue; }
                dic.Add(hashKey, item.Value);
            }
            var result = new HashScanResult<RedisValue>() {
                Key = key,
                Page = pageOffset + 1,
                Limit = pageSize,
                Items = dic,
            };
            // var isc = (IScanningCursor)result;
            return result;
        }
        #endregion
    }
}
